#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "disk_io.h"
#include "storage_system.h"

// 调试开关 - 取消注释以启用详细调试信息
// #define DEBUG_ENABLED

// 调试日志函数
#ifdef DEBUG_ENABLED
#define DEBUG_LOG(msg, ...) printf("[DEBUG] %s:%d: " msg "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#define DEBUG_HEXDUMP(data, len) debug_hexdump(data, len)
#else
#define DEBUG_LOG(msg, ...) /* 调试信息已禁用 */
#define DEBUG_HEXDUMP(data, len) /* 十六进制转储已禁用 */
#endif

// 测试结果记录
int total_tests = 0;
int passed_tests = 0;
int current_test_success = 1; // 默认测试成功

// 十六进制转储函数（用于调试）
void debug_hexdump(const uint8_t* data, size_t len) {
    size_t i, j;
    for (i = 0; i < len; i += 16) {
        printf("0x%04zx: ", i);
        for (j = 0; j < 16; j++) {
            if (i + j < len) {
                printf("%02x ", data[i + j]);
            } else {
                printf("   ");
            }
        }
        printf(" | ");
        for (j = 0; j < 16; j++) {
            if (i + j < len) {
                char c = data[i + j];
                printf("%c", (c >= 32 && c <= 126) ? c : '.');
            } else {
                printf(" ");
            }
        }
        printf("\n");
    }
}

// 测试函数模板
void test_case(const char* name, void (*test_func)()) {
    total_tests++;
    printf("\n=== Test: %s ===\n", name);
    
    // 重置测试成功标志
    current_test_success = 1;
    
    // 运行测试函数
    test_func();
    
    // 检查测试结果
    if (current_test_success) {
        passed_tests++;
        printf("PASS: %s\n", name);
    } else {
        printf("FAIL: %s\n", name);
    }
}

// 测试磁盘保存页面功能
void test_disk_save_page() {
    printf("\n测试磁盘保存页面功能...\n");
    
    // 准备测试数据
    const char* data_dir = "./data";
    page_id_t test_page_id = 100;
    uint8_t test_data[PAGE_SIZE];
    
    DEBUG_LOG("开始测试 disk_save_page 函数");
    DEBUG_LOG("数据目录: %s", data_dir);
    DEBUG_LOG("页面ID: %u", test_page_id);
    DEBUG_LOG("页面大小: %d 字节", PAGE_SIZE);
    
    // 填充测试数据
    DEBUG_LOG("开始填充测试数据");
    for (size_t i = 0; i < PAGE_SIZE; i++) {
        test_data[i] = (uint8_t)(i % 256);
    }
    
    // 显示部分测试数据用于调试
    DEBUG_LOG("测试数据前16字节:");
    DEBUG_HEXDUMP(test_data, 16);
    
    // 保存页面到磁盘
    DEBUG_LOG("调用 disk_save_page 函数");
    storage_error_t result = disk_save_page(data_dir, test_page_id, test_data);
    
    if (result != STORAGE_OK) {
        DEBUG_LOG("保存页面失败，错误码: %d", result);
        printf("错误: 保存页面失败，错误码: %d\n", result);
        current_test_success = 0;
        return;
    }
    
    DEBUG_LOG("页面保存成功，返回码: %d", result);
    printf("页面保存成功\n");
}

// 测试磁盘加载页面功能
void test_disk_load_page() {
    printf("\n测试磁盘加载页面功能...\n");
    
    // 准备测试数据
    const char* data_dir = "./data";
    page_id_t test_page_id = 100;
    uint8_t loaded_data[PAGE_SIZE];
    uint8_t expected_data[PAGE_SIZE];
    
    DEBUG_LOG("开始测试 disk_load_page 函数");
    DEBUG_LOG("数据目录: %s", data_dir);
    DEBUG_LOG("页面ID: %u", test_page_id);
    
    // 填充预期数据
    DEBUG_LOG("开始填充预期数据");
    for (size_t i = 0; i < PAGE_SIZE; i++) {
        expected_data[i] = (uint8_t)(i % 256);
    }
    
    // 从磁盘加载页面
    DEBUG_LOG("调用 disk_load_page 函数");
    storage_error_t result = disk_load_page(data_dir, test_page_id, loaded_data);
    
    if (result != STORAGE_OK) {
        DEBUG_LOG("加载页面失败，错误码: %d", result);
        printf("错误: 加载页面失败，错误码: %d\n", result);
        current_test_success = 0;
        return;
    }
    
    DEBUG_LOG("页面加载成功，返回码: %d", result);
    
    // 显示加载的数据的前16字节用于调试
    DEBUG_LOG("加载的数据前16字节:");
    DEBUG_HEXDUMP(loaded_data, 16);
    
    // 比较加载的数据与预期数据
    DEBUG_LOG("开始比较加载的数据与预期数据");
    size_t mismatch_count = 0;
    size_t first_mismatch = -1;
    
    // 快速检查 - 先比较前16字节和后16字节
    if (memcmp(loaded_data, expected_data, 16) != 0) {
        for (size_t i = 0; i < 16 && i < PAGE_SIZE; i++) {
            if (loaded_data[i] != expected_data[i]) {
                mismatch_count++;
                if (first_mismatch == (size_t)-1) first_mismatch = i;
            }
        }
    }
    
    // 如果快速检查通过，再进行完整比较
    if (mismatch_count == 0) {
        DEBUG_LOG("快速检查通过，进行完整数据比较");
        if (memcmp(loaded_data, expected_data, PAGE_SIZE) != 0) {
            // 找出不匹配的位置
            for (size_t i = 0; i < PAGE_SIZE; i++) {
                if (loaded_data[i] != expected_data[i]) {
                    mismatch_count++;
                    if (first_mismatch == (size_t)-1) {
                        first_mismatch = i;
                        DEBUG_LOG("发现第一个不匹配位置: 偏移 %zu, 期望值: 0x%02x, 实际值: 0x%02x", 
                                 i, expected_data[i], loaded_data[i]);
                        // 显示不匹配位置前后的数据
                        if (i >= 8) {
                            DEBUG_LOG("不匹配位置前8字节:");
                            DEBUG_HEXDUMP(&loaded_data[i-8], 8);
                        }
                        DEBUG_LOG("不匹配位置后8字节:");
                        DEBUG_HEXDUMP(&loaded_data[i], 8);
                        break; // 只显示第一个不匹配
                    }
                }
            }
        }
    }
    
    if (mismatch_count > 0) {
        DEBUG_LOG("数据不匹配，发现 %zu 处差异，第一个差异在偏移 %zu", mismatch_count, first_mismatch);
        printf("错误: 加载的数据与预期数据不匹配\n");
        current_test_success = 0;
        return;
    }
    
    DEBUG_LOG("数据比较完成，所有 %d 字节匹配", PAGE_SIZE);
    printf("页面加载成功，数据验证通过\n");
}

// 测试磁盘删除页面功能
void test_disk_delete_page() {
    printf("\n测试磁盘删除页面功能...\n");
    
    // 准备测试数据
    const char* data_dir = "./data";
    page_id_t test_page_id = 100;
    
    DEBUG_LOG("开始测试 disk_delete_page 函数");
    DEBUG_LOG("数据目录: %s", data_dir);
    DEBUG_LOG("页面ID: %u", test_page_id);
    
    // 获取文件名用于调试
    char* filename = disk_get_page_filename(data_dir, test_page_id);
    if (filename) {
        DEBUG_LOG("要删除的页面文件: %s", filename);
        free(filename);
    }
    
    // 从磁盘删除页面
    DEBUG_LOG("调用 disk_delete_page 函数");
    storage_error_t result = disk_delete_page(data_dir, test_page_id);
    
    if (result != STORAGE_OK) {
        DEBUG_LOG("删除页面失败，错误码: %d", result);
        printf("错误: 删除页面失败，错误码: %d\n", result);
        current_test_success = 0;
        return;
    }
    
    DEBUG_LOG("页面删除成功，返回码: %d", result);
    printf("页面删除成功\n");
    
    // 验证页面确实已删除
    DEBUG_LOG("验证页面文件是否已删除");
    filename = disk_get_page_filename(data_dir, test_page_id);
    if (!filename) {
        DEBUG_LOG("获取页面文件名失败");
        printf("错误: 获取页面文件名失败\n");
        current_test_success = 0;
        return;
    }
    
    DEBUG_LOG("尝试打开文件: %s", filename);
    FILE* file = fopen(filename, "rb");
    if (file != NULL) {
        DEBUG_LOG("文件依然存在，删除失败");
        printf("错误: 页面文件依然存在，删除操作未生效\n");
        fclose(file);
        free(filename);
        current_test_success = 0;
        return;
    }
    
    DEBUG_LOG("文件已成功删除，无法打开");
    free(filename);
    printf("页面文件验证已删除\n");
}

// 测试文件路径生成功能
void test_disk_get_page_filename() {
    printf("\n测试文件路径生成功能...\n");
    
    const char* data_dir = "./data";
    page_id_t test_page_id = 200;
    
    DEBUG_LOG("开始测试 disk_get_page_filename 函数");
    DEBUG_LOG("数据目录: %s", data_dir);
    DEBUG_LOG("页面ID: %u", test_page_id);
    
    char* filename = disk_get_page_filename(data_dir, test_page_id);
    if (!filename) {
        DEBUG_LOG("生成页面文件名失败");
        printf("错误: 生成页面文件名失败\n");
        current_test_success = 0;
        return;
    }
    
    DEBUG_LOG("生成的页面文件名为: %s", filename);
    printf("生成的页面文件名为: %s\n", filename);
    
    // 验证文件名格式
    char expected_filename[256];
    snprintf(expected_filename, sizeof(expected_filename), "%s/page_%u.dat", data_dir, test_page_id);
    DEBUG_LOG("预期的页面文件名为: %s", expected_filename);
    
    if (strcmp(filename, expected_filename) != 0) {
        DEBUG_LOG("文件名不匹配: 实际=%s, 预期=%s", filename, expected_filename);
        printf("错误: 生成的文件名与预期不符\n");
        printf("预期: %s\n", expected_filename);
        free(filename);
        current_test_success = 0;
        return;
    }
    
    DEBUG_LOG("文件名格式验证通过");
    free(filename);
    printf("文件名格式验证通过\n");
}

// 测试完整的保存-加载-删除流程
void test_disk_io_full_cycle() {
    printf("\n测试完整的保存-加载-删除流程...\n");
    
    const char* data_dir = "./data";
    page_id_t test_page_id = 300;
    uint8_t test_data[PAGE_SIZE];
    uint8_t loaded_data[PAGE_SIZE];
    
    DEBUG_LOG("开始测试完整的IO流程");
    DEBUG_LOG("数据目录: %s", data_dir);
    DEBUG_LOG("页面ID: %u", test_page_id);
    
    // 填充测试数据
    DEBUG_LOG("开始填充测试数据");
    for (size_t i = 0; i < PAGE_SIZE; i++) {
        test_data[i] = (uint8_t)(i * 3 % 256);
    }
    
    DEBUG_LOG("测试数据前16字节:");
    DEBUG_HEXDUMP(test_data, 16);
    
    // 1. 保存页面
    DEBUG_LOG("阶段1: 保存页面");
    storage_error_t result = disk_save_page(data_dir, test_page_id, test_data);
    if (result != STORAGE_OK) {
        DEBUG_LOG("保存页面失败，错误码: %d", result);
        printf("错误: 保存页面失败，错误码: %d\n", result);
        current_test_success = 0;
        return;
    }
    printf("1. 页面保存成功\n");
    
    // 2. 加载页面
    DEBUG_LOG("阶段2: 加载页面");
    result = disk_load_page(data_dir, test_page_id, loaded_data);
    if (result != STORAGE_OK) {
        DEBUG_LOG("加载页面失败，错误码: %d", result);
        printf("错误: 加载页面失败，错误码: %d\n", result);
        current_test_success = 0;
        return;
    }
    printf("2. 页面加载成功\n");
    
    DEBUG_LOG("加载的数据前16字节:");
    DEBUG_HEXDUMP(loaded_data, 16);
    
    // 3. 验证数据
    DEBUG_LOG("阶段3: 验证数据");
    if (memcmp(loaded_data, test_data, PAGE_SIZE) != 0) {
        DEBUG_LOG("数据验证失败");
        printf("错误: 加载的数据与保存的数据不匹配\n");
        
        // 查找第一个不匹配位置
        for (size_t i = 0; i < PAGE_SIZE; i++) {
            if (loaded_data[i] != test_data[i]) {
                DEBUG_LOG("第一个不匹配位置: 偏移 %zu, 保存值: 0x%02x, 加载值: 0x%02x", 
                         i, test_data[i], loaded_data[i]);
                break;
            }
        }
        
        current_test_success = 0;
        return;
    }
    printf("3. 数据验证通过\n");
    
    // 4. 删除页面
    DEBUG_LOG("阶段4: 删除页面");
    result = disk_delete_page(data_dir, test_page_id);
    if (result != STORAGE_OK) {
        DEBUG_LOG("删除页面失败，错误码: %d", result);
        printf("错误: 删除页面失败，错误码: %d\n", result);
        current_test_success = 0;
        return;
    }
    printf("4. 页面删除成功\n");
    
    // 验证删除
    DEBUG_LOG("验证页面是否已删除");
    char* filename = disk_get_page_filename(data_dir, test_page_id);
    if (filename) {
        FILE* file = fopen(filename, "rb");
        if (file != NULL) {
            DEBUG_LOG("文件依然存在，删除验证失败");
            fclose(file);
            free(filename);
            printf("错误: 页面删除验证失败，文件依然存在\n");
            current_test_success = 0;
            return;
        }
        free(filename);
    }
    
    DEBUG_LOG("完整IO流程测试通过");
    printf("完整流程测试通过\n");
}

// 主函数
int main(int argc, char* argv[]) {
    printf("\n========== 磁盘IO测试程序 ==========\n");
    
    // 检查是否启用了调试模式
    #ifdef DEBUG_ENABLED
        printf("[注意] 调试模式已启用，将输出详细日志信息\n");
    #else
        printf("[注意] 调试模式已禁用，如需详细日志，请在代码中启用 DEBUG_ENABLED\n");
    #endif
    
    // 确保数据目录存在
    #ifdef _WIN32
        DEBUG_LOG("创建数据目录 (Windows)");
        system("mkdir data 2>NUL");
    #else
        DEBUG_LOG("创建数据目录 (Unix-like)");
        system("mkdir -p ./data");
    #endif
    
    // 运行各个测试用例
    test_case("磁盘保存页面", test_disk_save_page);
    test_case("磁盘加载页面", test_disk_load_page);
    test_case("磁盘删除页面", test_disk_delete_page);
    test_case("文件路径生成", test_disk_get_page_filename);
    test_case("完整IO流程", test_disk_io_full_cycle);
    
    // 输出测试结果摘要
    printf("\n========== 测试结果摘要 ==========\n");
    printf("总测试数: %d\n", total_tests);
    printf("通过测试数: %d\n", passed_tests);
    printf("失败测试数: %d\n", total_tests - passed_tests);
    
    if (total_tests - passed_tests == 0) {
        printf("\n🎉 所有测试通过！\n");
    } else {
        printf("\n❌ 有测试失败，请检查问题。\n");
    }
    
    return (total_tests - passed_tests == 0) ? 0 : 1;
}